aviutl2\generic\binding/
edit_section.rs

1use crate::common::Rational32;
2
3/// オブジェクトへのハンドル。
4#[derive(Debug, Clone, Copy)]
5pub struct ObjectHandle {
6    pub(crate) internal: aviutl2_sys::plugin2::OBJECT_HANDLE,
7}
8impl From<aviutl2_sys::plugin2::OBJECT_HANDLE> for ObjectHandle {
9    fn from(value: aviutl2_sys::plugin2::OBJECT_HANDLE) -> Self {
10        Self { internal: value }
11    }
12}
13impl From<ObjectHandle> for aviutl2_sys::plugin2::OBJECT_HANDLE {
14    fn from(value: ObjectHandle) -> Self {
15        value.internal
16    }
17}
18
19// 動いたし、このObjectHandleをグローバルに持っておくことも想定されてそうなので多分大丈夫なはず
20unsafe impl Send for ObjectHandle {}
21unsafe impl Sync for ObjectHandle {}
22
23/// 編集情報構造体。
24///
25/// # Note
26///
27/// UI表示と異なり、フレーム番号・レイヤー番号は0始まりです。
28#[derive(Debug, Clone, Copy)]
29pub struct EditInfo {
30    /// シーンの幅。
31    pub width: usize,
32    /// シーンの高さ。
33    pub height: usize,
34    /// フレームレート。
35    pub fps: Rational32,
36    /// サンプルレート。
37    pub sample_rate: usize,
38    /// 現在のカーソルのフレーム番号。
39    pub frame: usize,
40    /// 現在の選択レイヤー番号。
41    pub layer: usize,
42    /// オブジェクトが存在する最大のフレーム番号。
43    pub frame_max: usize,
44    /// オブジェクトが存在する最大のレイヤー番号。
45    pub layer_max: usize,
46}
47
48impl EditInfo {
49    /// # Safety
50    ///
51    /// `ptr`は有効な`EDIT_INFO`ポインタである必要があります。
52    pub unsafe fn from_ptr(ptr: *const aviutl2_sys::plugin2::EDIT_INFO) -> Self {
53        let raw = unsafe { &*ptr };
54        Self {
55            width: raw.width as usize,
56            height: raw.height as usize,
57            fps: Rational32::new(raw.rate, raw.scale),
58            sample_rate: raw.sample_rate as usize,
59            frame: raw.frame as usize,
60            layer: raw.layer as usize,
61            frame_max: raw.frame_max as usize,
62            layer_max: raw.layer_max as usize,
63        }
64    }
65}
66
67/// オブジェクトのレイヤーとフレーム情報。
68#[derive(Debug, Clone, Copy)]
69pub struct ObjectLayerFrame {
70    pub layer: usize,
71    pub start: usize,
72    pub end: usize,
73}
74
75/// [`EditSection`] 関連のエラー。
76#[derive(thiserror::Error, Debug)]
77pub enum EditSectionError {
78    #[error("api call failed")]
79    ApiCallFailed,
80    #[error("object does not exist")]
81    ObjectDoesNotExist,
82    #[error("input utf-8 string contains null byte")]
83    InputCstrContainsNull(#[from] std::ffi::NulError),
84    #[error("input utf-16 string contains null byte")]
85    InputCwstrContainsNull(#[from] crate::common::NullByteError),
86    #[error("value is out of range")]
87    ValueOutOfRange(#[from] std::num::TryFromIntError),
88    #[error("api returned non-utf8 data")]
89    NonUtf8Data(#[from] std::str::Utf8Error),
90
91    #[cfg(feature = "aviutl2-alias")]
92    #[error("alias parse error: {0}")]
93    ParseFailed(#[from] aviutl2_alias::TableParseError),
94}
95
96pub type EditSectionResult<T> = Result<T, EditSectionError>;
97
98/// 編集セクションのハンドル。
99#[derive(Debug)]
100pub struct EditSection {
101    /// 編集情報。
102    pub info: EditInfo,
103
104    pub(crate) internal: *mut aviutl2_sys::plugin2::EDIT_SECTION,
105}
106
107impl EditSection {
108    /// 生ポインタから `EditSection` を作成します。
109    ///
110    /// # Safety
111    ///
112    /// 有効な `EDIT_SECTION` ポインタである必要があります。
113    pub unsafe fn from_ptr(ptr: *mut aviutl2_sys::plugin2::EDIT_SECTION) -> Self {
114        Self {
115            internal: ptr,
116            info: unsafe { EditInfo::from_ptr((*ptr).info) },
117        }
118    }
119
120    /// オブジェクトエイリアスから指定の位置にオブジェクトを作成します。
121    ///
122    /// # Arguments
123    ///
124    /// - `alias`:オブジェクトエイリアスのデータ。オブジェクトエイリアスと同じフォーマットで指定します。
125    /// - `layer`:作成するオブジェクトのレイヤー番号(0始まり)。
126    /// - `frame`:作成するオブジェクトのフレーム番号(0始まり)。
127    /// - `length`:作成するオブジェクトの長さ(フレーム数)。
128    ///
129    /// lengthはエイリアスデータにフレーム情報が無い場合に利用されます。
130    ///
131    /// # Errors
132    ///
133    /// エイリアスの変換に失敗した場合、またはオブジェクトが既存のオブジェクトに重なる場合にエラー
134    ///
135    pub fn create_object_from_alias(
136        &self,
137        alias: &str,
138        layer: usize,
139        frame: usize,
140        length: usize,
141    ) -> EditSectionResult<ObjectHandle> {
142        let c_alias = std::ffi::CString::new(alias)?;
143        let object_handle = unsafe {
144            ((*self.internal).create_object_from_alias)(
145                c_alias.as_ptr(),
146                layer.try_into()?,
147                frame.try_into()?,
148                length.try_into()?,
149            )
150        };
151        if object_handle.is_null() {
152            return Err(EditSectionError::ApiCallFailed);
153        }
154        Ok(ObjectHandle {
155            internal: object_handle,
156        })
157    }
158
159    /// 指定のフレーム番号以降にあるオブジェクトを検索します。
160    ///
161    /// # Arguments
162    ///
163    /// - `layer`:検索するレイヤー番号(0始まり)。
164    /// - `frame`:検索を開始するフレーム番号(0始まり)。
165    pub fn find_object_after(
166        &self,
167        layer: usize,
168        frame: usize,
169    ) -> EditSectionResult<Option<ObjectHandle>> {
170        let object_handle =
171            unsafe { ((*self.internal).find_object)(layer.try_into()?, frame.try_into()?) };
172        if object_handle.is_null() {
173            Ok(None)
174        } else {
175            Ok(Some(ObjectHandle {
176                internal: object_handle,
177            }))
178        }
179    }
180
181    /// オブジェクトに対象エフェクトが何個存在するかを取得します。
182    ///
183    /// # Arguments
184    ///
185    /// - `object`:対象のオブジェクトハンドル。
186    /// - `effect`:対象のエフェクト名。(エイリアスファイルの effect.name の値)
187    ///
188    /// # Returns
189    ///
190    /// 対象エフェクトの数。存在しない場合は0を返します。
191    pub fn count_object_effect(
192        &self,
193        object: &ObjectHandle,
194        effect: &str,
195    ) -> EditSectionResult<usize> {
196        self.ensure_object_exists(object)?;
197        let c_effect = crate::common::CWString::new(effect)?;
198        let count =
199            unsafe { ((*self.internal).count_object_effect)(object.internal, c_effect.as_ptr()) };
200        Ok(count.try_into()?)
201    }
202
203    /// 指定のオブジェクトのレイヤーとフレーム情報を取得します。
204    pub fn get_object_layer_frame(
205        &self,
206        object: &ObjectHandle,
207    ) -> EditSectionResult<ObjectLayerFrame> {
208        self.ensure_object_exists(object)?;
209        let object = unsafe { ((*self.internal).get_object_layer_frame)(object.internal) };
210        Ok(ObjectLayerFrame {
211            layer: object.layer.try_into()?,
212            start: object.start.try_into()?,
213            end: object.end.try_into()?,
214        })
215    }
216
217    /// オブジェクトの情報をエイリアスデータとして取得します。
218    pub fn get_object_alias(&self, object: &ObjectHandle) -> EditSectionResult<String> {
219        self.ensure_object_exists(object)?;
220        let alias_ptr = unsafe { ((*self.internal).get_object_alias)(object.internal) };
221        if alias_ptr.is_null() {
222            return Err(EditSectionError::ApiCallFailed);
223        }
224        let c_str = unsafe { std::ffi::CStr::from_ptr(alias_ptr) };
225        let alias = c_str.to_str()?.to_owned();
226        Ok(alias)
227    }
228
229    /// オブジェクトの設定項目の値を文字列で取得します。
230    ///
231    /// # Arguments
232    ///
233    /// - `object`:対象のオブジェクトハンドル。
234    /// - `effect_name`:設定項目の名前。
235    /// - `effect_index`:同じ名前の設定項目が複数ある場合のインデックス(0始まり)。
236    /// - `item`:設定項目の名前。(エイリアスファイルのキーの名前)
237    pub fn get_object_effect_item(
238        &self,
239        object: &ObjectHandle,
240        effect_name: &str,
241        effect_index: usize,
242        item: &str,
243    ) -> EditSectionResult<String> {
244        self.ensure_object_exists(object)?;
245        let c_effect_name = crate::common::CWString::new(&format!("{effect_name}:{effect_index}"))?;
246        let c_item = crate::common::CWString::new(item)?;
247        let value_ptr = unsafe {
248            ((*self.internal).get_object_item_value)(
249                object.internal,
250                c_effect_name.as_ptr(),
251                c_item.as_ptr(),
252            )
253        };
254        if value_ptr.is_null() {
255            return Err(EditSectionError::ApiCallFailed);
256        }
257        let c_str = unsafe { std::ffi::CStr::from_ptr(value_ptr) };
258        let value = c_str.to_str()?.to_owned();
259        Ok(value)
260    }
261
262    /// オブジェクトの設定項目の値を文字列で設定します。
263    ///
264    /// # Arguments
265    ///
266    /// - `object`:対象のオブジェクトハンドル。
267    /// - `effect_name`:設定項目の名前。
268    /// - `effect_index`:同じ名前の設定項目が複数ある場合のインデックス(0始まり)。
269    /// - `item`:設定項目の名前。(エイリアスファイルのキーの名前)
270    /// - `value`:設定する値。
271    pub fn set_object_effect_item(
272        &self,
273        object: &ObjectHandle,
274        effect_name: &str,
275        effect_index: usize,
276        item: &str,
277        value: &str,
278    ) -> EditSectionResult<()> {
279        self.ensure_object_exists(object)?;
280        let c_effect_name = crate::common::CWString::new(&format!("{effect_name}:{effect_index}"))?;
281        let c_item = crate::common::CWString::new(item)?;
282        let c_value = std::ffi::CString::new(value)?;
283        let success = unsafe {
284            ((*self.internal).set_object_item_value)(
285                object.internal,
286                c_effect_name.as_ptr(),
287                c_item.as_ptr(),
288                c_value.as_ptr(),
289            )
290        };
291        if !success {
292            return Err(EditSectionError::ApiCallFailed);
293        }
294        Ok(())
295    }
296
297    /// オブジェクトを移動します。
298    pub fn move_object(
299        &self,
300        object: &ObjectHandle,
301        new_layer: usize,
302        new_start_frame: usize,
303    ) -> EditSectionResult<()> {
304        self.ensure_object_exists(object)?;
305        let success = unsafe {
306            ((*self.internal).move_object)(
307                object.internal,
308                new_layer.try_into()?,
309                new_start_frame.try_into()?,
310            )
311        };
312        if !success {
313            return Err(EditSectionError::ApiCallFailed);
314        }
315        Ok(())
316    }
317
318    /// オブジェクトを削除します。
319    pub fn delete_object(&self, object: &ObjectHandle) -> EditSectionResult<()> {
320        self.ensure_object_exists(object)?;
321        unsafe { ((*self.internal).delete_object)(object.internal) };
322        Ok(())
323    }
324
325    /// 現在、オブジェクト設定ウィンドウで選択されているオブジェクトを取得します。
326    pub fn get_focused_object(&self) -> EditSectionResult<Option<ObjectHandle>> {
327        let object_handle = unsafe { ((*self.internal).get_focus_object)() };
328        if object_handle.is_null() {
329            Ok(None)
330        } else {
331            Ok(Some(ObjectHandle {
332                internal: object_handle,
333            }))
334        }
335    }
336
337    /// 現在選択されているオブジェクトの一覧を取得します。
338    pub fn get_selected_objects(&self) -> EditSectionResult<Vec<ObjectHandle>> {
339        let mut handles = Vec::new();
340        let num_objects = unsafe { ((*self.internal).get_selected_object_num)() };
341        for i in 0..num_objects {
342            let object_handle = unsafe { ((*self.internal).get_selected_object)(i) };
343            if object_handle.is_null() {
344                return Err(EditSectionError::ApiCallFailed);
345            }
346            handles.push(ObjectHandle {
347                internal: object_handle,
348            });
349        }
350        Ok(handles)
351    }
352
353    /// オブジェクト設定ウィンドウで指定のオブジェクトを選択状態にします。
354    ///
355    /// # Note
356    ///
357    /// コールバック処理の終了時に設定されます。
358    pub fn focus_object(&self, object: &ObjectHandle) -> EditSectionResult<()> {
359        self.ensure_object_exists(object)?;
360        unsafe { ((*self.internal).set_focus_object)(object.internal) };
361        Ok(())
362    }
363
364    /// オブジェクトが存在するかどうか調べます。
365    ///
366    /// # Note
367    ///
368    /// 内部的には、get_object_layer_frame を呼び出してレイヤー番号が -1 でないかを確認しています。
369    pub fn object_exists(&self, object: &ObjectHandle) -> bool {
370        let object = unsafe { ((*self.internal).get_object_layer_frame)(object.internal) };
371        object.layer != -1
372    }
373
374    fn ensure_object_exists(&self, object: &ObjectHandle) -> EditSectionResult<()> {
375        if !self.object_exists(object) {
376            return Err(EditSectionError::ObjectDoesNotExist);
377        }
378        Ok(())
379    }
380
381    #[doc(hidden)]
382    #[expect(private_bounds)]
383    pub fn __output_log_if_error<T: MenuCallbackReturn>(&self, result: T) {
384        if let Some(err_msg) = result.into_optional_error() {
385            let _ = crate::logger::write_error_log(&err_msg);
386        }
387    }
388
389    #[doc(hidden)]
390    #[expect(private_bounds)]
391    pub fn __alert_if_error<T: MenuCallbackReturn>(&self, result: T) {
392        if let Some(err_msg) = result.into_optional_error() {
393            crate::common::alert_error(&anyhow::anyhow!(err_msg));
394        }
395    }
396
397    /// すべてのレイヤーをイテレータで取得します。
398    pub fn layers(&self) -> EditSectionLayersIterator<'_> {
399        EditSectionLayersIterator::new(self)
400    }
401
402    /// [EditSectionLayerCaller] を作成します。
403    pub fn layer<'a>(&'a self, layer: usize) -> EditSectionLayerCaller<'a> {
404        EditSectionLayerCaller::new(self, layer)
405    }
406    /// [EditSectionObjectCaller] を作成します。
407    pub fn object<'a>(&'a self, object: &'a ObjectHandle) -> EditSectionObjectCaller<'a> {
408        EditSectionObjectCaller::new(self, object)
409    }
410}
411
412/// オブジェクト主体で関数を呼び出すための構造体。
413/// EditSection と ObjectHandle の組をまとめ、対象オブジェクトに対する
414/// 操作を簡潔に呼び出せるようにします。
415pub struct EditSectionObjectCaller<'a> {
416    edit_section: &'a EditSection,
417    pub handle: &'a ObjectHandle,
418}
419impl<'a> EditSectionObjectCaller<'a> {
420    pub fn new(edit_section: &'a EditSection, object: &'a ObjectHandle) -> Self {
421        Self {
422            edit_section,
423            handle: object,
424        }
425    }
426
427    /// オブジェクトのレイヤーとフレーム情報を取得します。
428    pub fn get_layer_frame(&self) -> EditSectionResult<ObjectLayerFrame> {
429        self.edit_section.get_object_layer_frame(self.handle)
430    }
431    /// オブジェクトの情報をエイリアスデータとして取得します。
432    pub fn get_alias(&self) -> EditSectionResult<String> {
433        self.edit_section.get_object_alias(self.handle)
434    }
435    /// オブジェクトの情報をエイリアスデータとして取得し、パースします。
436    #[cfg(feature = "aviutl2-alias")]
437    pub fn get_alias_parsed(&self) -> EditSectionResult<aviutl2_alias::Table> {
438        self.edit_section
439            .get_object_alias(self.handle)?
440            .parse()
441            .map_err(Into::into)
442    }
443
444    /// オブジェクトに対象エフェクトが何個存在するかを取得します。
445    ///
446    /// # Arguments
447    ///
448    /// - `effect`:対象のエフェクト名。(エイリアスファイルの effect.name の値)
449    ///
450    /// # Returns
451    ///
452    /// 対象エフェクトの数。存在しない場合は0を返します。
453    pub fn count_effect(&self, effect: &str) -> EditSectionResult<usize> {
454        self.edit_section.count_object_effect(self.handle, effect)
455    }
456
457    /// オブジェクトの設定項目の値を文字列で取得します。
458    ///
459    /// # Arguments
460    ///
461    /// - `effect_name`:設定項目の名前。
462    /// - `effect_index`:同じ名前の設定項目が複数ある場合のインデックス(0始まり)。
463    /// - `item`:設定項目の名前。(エイリアスファイルのキーの名前)
464    pub fn get_effect_item(
465        &self,
466        effect_name: &str,
467        effect_index: usize,
468        item: &str,
469    ) -> EditSectionResult<String> {
470        self.edit_section
471            .get_object_effect_item(self.handle, effect_name, effect_index, item)
472    }
473
474    /// オブジェクトの設定項目の値を文字列で設定します。
475    ///
476    /// # Arguments
477    ///
478    /// - `effect_name`:設定項目の名前。
479    /// - `effect_index`:同じ名前の設定項目が複数ある場合のインデックス(0始まり)。
480    /// - `item`:設定項目の名前。(エイリアスファイルのキーの名前)
481    /// - `value`:設定する値。
482    pub fn set_effect_item(
483        &self,
484        effect_name: &str,
485        effect_index: usize,
486        item: &str,
487        value: &str,
488    ) -> EditSectionResult<()> {
489        self.edit_section.set_object_effect_item(
490            self.handle,
491            effect_name,
492            effect_index,
493            item,
494            value,
495        )
496    }
497
498    /// オブジェクトを移動します。
499    ///
500    /// # Arguments
501    ///
502    /// - `new_layer`:移動先のレイヤー番号(0始まり)。
503    /// - `new_start_frame`:移動先の開始フレーム番号(0始まり)。
504    pub fn move_object(&self, new_layer: usize, new_start_frame: usize) -> EditSectionResult<()> {
505        self.edit_section
506            .move_object(self.handle, new_layer, new_start_frame)
507    }
508
509    /// オブジェクトを削除します。
510    pub fn delete_object(&self) -> EditSectionResult<()> {
511        self.edit_section.delete_object(self.handle)
512    }
513
514    /// オブジェクト設定ウィンドウでこのオブジェクトを選択状態にします。
515    ///
516    /// # Note
517    ///
518    /// コールバック処理の終了時に設定されます。
519    pub fn focus_object(&self) -> EditSectionResult<()> {
520        self.edit_section.focus_object(self.handle)
521    }
522
523    /// このオブジェクトが存在するかどうか調べます。
524    pub fn exists(&self) -> bool {
525        self.edit_section.object_exists(self.handle)
526    }
527}
528
529/// レイヤー主体で関数を呼び出すための構造体。
530/// EditSection と レイヤー番号 の組をまとめ、対象レイヤーに対する
531/// 操作を簡潔に呼び出せるようにします。
532pub struct EditSectionLayerCaller<'a> {
533    edit_section: &'a EditSection,
534    pub index: usize,
535}
536impl<'a> EditSectionLayerCaller<'a> {
537    pub fn new(edit_section: &'a EditSection, layer: usize) -> Self {
538        Self {
539            edit_section,
540            index: layer,
541        }
542    }
543    /// 指定のフレーム番号以降にあるオブジェクトを検索します。
544    ///
545    /// # Arguments
546    ///
547    /// - `frame`:検索を開始するフレーム番号(0始まり)。
548    pub fn find_object_after(&self, frame: usize) -> EditSectionResult<Option<ObjectHandle>> {
549        self.edit_section.find_object_after(self.index, frame)
550    }
551
552    /// このレイヤーに存在するすべてのオブジェクトを、
553    /// 開始フレームの昇順で走査するイテレータを返します。
554    pub fn objects(&self) -> EditSectionLayerObjectsIterator<'a> {
555        EditSectionLayerObjectsIterator::new(self.edit_section, self.index)
556    }
557}
558
559/// レイヤーのイテレータ。
560#[derive(Debug, Clone)]
561pub struct EditSectionLayersIterator<'a> {
562    edit_section: &'a EditSection,
563    current: usize,
564    total: usize,
565}
566
567impl<'a> EditSectionLayersIterator<'a> {
568    fn new(edit_section: &'a EditSection) -> Self {
569        Self {
570            edit_section,
571            current: 0,
572            total: edit_section.info.layer_max,
573        }
574    }
575}
576
577impl<'a> Iterator for EditSectionLayersIterator<'a> {
578    type Item = EditSectionLayerCaller<'a>;
579
580    fn next(&mut self) -> Option<Self::Item> {
581        if self.current > self.total {
582            return None;
583        }
584        let layer = self.current;
585        self.current += 1;
586        Some(EditSectionLayerCaller::new(self.edit_section, layer))
587    }
588}
589
590/// レイヤー内のオブジェクトを走査するイテレータ。
591/// アイテムは `(オブジェクトのレイヤー・フレーム情報, ハンドル)` の組です。
592#[derive(Debug, Clone)]
593pub struct EditSectionLayerObjectsIterator<'a> {
594    edit_section: &'a EditSection,
595    layer: usize,
596    next_frame: usize,
597}
598
599impl<'a> EditSectionLayerObjectsIterator<'a> {
600    fn new(edit_section: &'a EditSection, layer: usize) -> Self {
601        Self {
602            edit_section,
603            layer,
604            next_frame: 0,
605        }
606    }
607}
608
609impl<'a> Iterator for EditSectionLayerObjectsIterator<'a> {
610    type Item = (ObjectLayerFrame, ObjectHandle);
611
612    fn next(&mut self) -> Option<Self::Item> {
613        // 検索・取得でエラーが出た場合は None を返して終了する。
614        let Ok(Some(handle)) = self
615            .edit_section
616            .find_object_after(self.layer, self.next_frame)
617        else {
618            return None;
619        };
620
621        let lf = match self.edit_section.get_object_layer_frame(&handle) {
622            Ok(lf) => lf,
623            Err(_) => return None,
624        };
625
626        // 次の検索開始位置を、いま見つかったオブジェクトの末尾+1 に進める。
627        self.next_frame = lf.end.saturating_add(1);
628
629        Some((lf, handle))
630    }
631}
632
633trait MenuCallbackReturn {
634    fn into_optional_error(self) -> Option<String>;
635}
636impl<E> MenuCallbackReturn for Result<(), E>
637where
638    Box<dyn std::error::Error>: From<E>,
639{
640    fn into_optional_error(self) -> Option<String> {
641        match self {
642            Ok(_) => None,
643            Err(e) => {
644                let boxed: Box<dyn std::error::Error> = e.into();
645                Some(format!("{}", boxed))
646            }
647        }
648    }
649}
650impl MenuCallbackReturn for () {
651    fn into_optional_error(self) -> Option<String> {
652        None
653    }
654}